#ifndef DATABASE_ZSETOBJECT_H
#define DATABASE_ZSETOBJECT_H

#include "Object.hpp"
#include "../Structurt/Skiplist.hpp"

#include <functional>
#include <memory>
#include <unordered_map>
#include <vector>


namespace DBServer
{
namespace Database
{

class ZsetObject : public Object {
public:
    using CallBack = std::function<void(ZsetObject&, CommandRequest&&, CommandRespond&)>;

    ZsetObject()
    :Object(ZSET_TYPE)
    {}
    ~ZsetObject() = default;

    void command_excute(CommandRespond& rep, CommandRequest&& req, com_func_t& cft);

public:
    typedef std::shared_ptr<std::string> Data;
    typedef Skiplist<Data> Skiplist_;

    /**
     * the command of ZsetObject:
     * 1.GET        KEY   
     *      get all element in object
     * 2.ZADD       KEY     SCORE   DATA
     *      push a element 
     * 3.ZLEN       KEY
     *      get len
     * 4.ZCOUNT     KEY     RANGE1  RANGE2
     *      count the number of element in range
     * 5.ZRANK      KEY     SCORE   DATA
     *      get the element's rank in the skiplist
     * 6.ZSCORE     KEY     SCORE
     *      get the element in the map by score
     * 7.ZREMOVE    KEY     SCORE
     *      delete a element in both of skiplist and map
     * 
    */
    inline auto get() -> std::vector<std::string>
    {
        std::vector<std::string> v;

        for (auto entry : dict)
        {
            v.push_back(*(entry.second));
        }

        return v;
    }

    inline auto zadd(double score, std::string&& data) -> bool
    {
        auto _data = std::make_shared<std::string>(std::move(data));
        
        if (dict.find(score) != dict.end())
        {
            return false;
        }

        dict.insert({score, _data});
        sl.insert(_data, score);
        return true;
    }

    inline auto zlen() -> int
    {
        return sl.size();
    }

    inline auto zcount(int range_1, int range_2) -> int 
    {
        auto begin = sl.get_score_first_in_range(range_1, range_2);
        auto end = sl.get_score_last_in_range(range_1, range_2);
        
        int first_index, last_index;
        if (begin == nullptr)
        {
            return 0;
        }
        first_index = sl.get_location(begin->data, begin->score);
        
        if (last_index != -1)
        {
            last_index = sl.get_location(end->data, end->score);
        }
        else
        {
            return sl.size() - first_index;
        }

        return last_index - first_index + 1;
    }

    inline auto zrank(double score, std::string&& data) -> int
    {
        if (dict.find(score) == dict.end())
        {
            return -1;
        }
        return sl.get_location(std::make_shared<std::string>(data), score);
    }

    inline auto zscore(double score, std::string& ret) -> bool
    {
        if (dict.find(score) == dict.end())
        {
            return false;
        }
        ret = *(dict[score]);
        return true;
    }

    inline auto zremove(double score, std::string& data) -> bool
    {
        if (dict.find(score) == dict.end())
        {
            return false;
        }

        sl.erase(dict[score], score);
        dict.erase(score);

        return true;
    }

private:
    // dict and sl sharing data by std::shared_ptr
    std::unordered_map<double, Data> dict;
    Skiplist_ sl;
    static std::unordered_map<int, CallBack> cb_table;
};
    
} // namespace Database
} // namespace DBServer



#endif